#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>

#define LEFT    30000000
#define RIGHT   30000200
#define THRNUM  4

// 存放任务的变量
static int number;

// 多个线程访问互斥量
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 定义一个结构体，使用结构体的方式进行传递参数
struct data_st{
	int num;
};

// 需要定义在main上面，否则找不到，或者先声明
// 定义子线程的行为
static void *calculate(void *p)
{
	int i, j, mark, no;
    no = ((struct data_st *)p)->num; // 线程号
	printf("[%d] thread is working!!! \n", no);

	while (1) {
		pthread_mutex_lock(&mutex);
		while(number == 0) // 子线程没有任务，出让调度器去请求main线程去分配任务
		{
			// 解锁，并进行等待
			pthread_cond_wait(&cond, &mutex);
			/*
			pthread_mutex_lock(&mutex);
			sched_yield();
			pthread_mutex_unlock(&mutex);
			*/
		}
		if(number == -1){
			/*
			 * 重点说明：
			 * lock和unlock是成对出现，这中间的代码属于临界区，如果有break，continue，goto，longjmp要跳出临界区的时候，需要提前释放锁
			 * */
			pthread_mutex_unlock(&mutex);
			break;
		}

    	// 新任务已经分配下来，继续执行
		i = number;
		number = 0;
		pthread_cond_broadcast(&cond);	// 通知main线程去
		pthread_mutex_unlock(&mutex);

		mark = 1;
    	for (j = 2; j < i/2; j++) {
        	if (i % j == 0) {
            	mark = 0;
            	break;
        	}
    	}

    	if (mark)
        	printf("[%d] %d is a primer\n", no, i);
	}
	// 退出的时候，将结构体指针传递出去，用于内存的释放
    pthread_exit(p);

}
/*
 * gcc pthread_distribute_pool_query.c -lpthread
 * 实现，main线程分配任务，4个子线程执行任务
 * */
int main(int argc, char **argv)
{
	int i, err;
	pthread_t tids[THRNUM];
	struct data_st *parma;
	void *ptr; // 定义一个指针，用于接收线程关闭后，返回出来的结构体指针

	// 创建任务线程
	for (i = 0; i < THRNUM; ++i) {
		// 申请一块空间保存参数，这样，每个线程都会有一个参数的内存空间
		parma = malloc(sizeof(*parma));
		if(parma == NULL)
		{
			perror("malloc()");
			exit(1);
		}
		parma->num = i;
		// 传递申请的结构体指针地址
		err = pthread_create(tids + i, NULL, calculate, parma);
		if (err) {
            fprintf(stderr, "pthread_create(): %s\n", strerror(err));
            exit(1);
        }	
	}

	// 生产任务
	for (i = LEFT; i <= RIGHT; ++i) {
		// 分配任务之前，先main线程锁住，就算这个时候锁已经被子线程拿到，当子线程释放了锁，main再进行锁，防止子线程去改变number
		pthread_mutex_lock(&mutex);
		while(number != 0)
		{
			// 等待子线程去把任务消耗掉
			pthread_cond_wait(&cond, &mutex);
		}
		number = i;
		// 通知子线程去解锁，并且告诉子线程有新任务
		pthread_cond_broadcast(&cond);
		pthread_mutex_unlock(&mutex);
	}
	
	// 任务结束，通知任务线程退出
	pthread_mutex_lock(&mutex);
	while(number != 0) // 不等于0，表示有任务还没有被消耗完成，继续出让调度器
	{
		pthread_cond_wait(&cond, &mutex);
	}
	// 任务已经消耗完成，-1，表示通知子线程去退出线程
	number = -1;
	pthread_cond_broadcast(&cond);
	pthread_mutex_unlock(&mutex);

	/*
	 * 考虑一个问题：线程最大能创建多少个？
	 * ulimit -a 查看当前系统限制
	 * 线程共用代码区和堆，但是会创建自己的栈
	 * 在32位机器上，栈的大小与剩余的内存空间一起决定共可以创建线程的数量
	 * 在64位机器上，因为用户空间会很大，有128t，因此线程数量大小的是pid的数量和栈的大小共同决定。
	 * */
	for (i = 0; i < THRNUM; ++i) {
        pthread_join(tids[i], &ptr);
		// 收尸完成后，需要将线程退出结果中返回过来的结构体指针，进行内存回收
		free(ptr);
    }

	// 销毁锁
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);

	exit(0);
}

